﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Diagnostics.Contracts;

namespace Galaxy
{
    public class GalaxyBitConverter
    {
        static char[] s_toStringChArray = new char[128];
        public static byte[] GetBytesBool(bool value)
        {
            return BitConverter.GetBytes(value);
        }
        public static byte[] GetBytesChar(char value)
        {
            return BitConverter.GetBytes(value);
        }
        public static byte[] GetBytesInt16(short value)
        {
            return BitConverter.GetBytes(value);
        }
        public static byte[] GetBytesInt32(int value)
        {
            return BitConverter.GetBytes(value);
        }
        public static byte[] GetBytesInt64(long value)
        {
            return BitConverter.GetBytes(value);
        }
        public static byte[] GetBytesUInt16(ushort value)
        {
            return BitConverter.GetBytes(value);
        }
        public static byte[] GetBytesUInt32(uint value)
        {
            return BitConverter.GetBytes(value);
        }
        public static byte[] GetBytesUInt64(ulong value)
        {
            return BitConverter.GetBytes(value);
        }

        public static void WriteBytesBool(byte[] buff, int offset, bool value)
        {
            if (buff.Length >= offset + sizeof(byte))
            {
                buff[offset] = value ? (byte)1 : (byte)0;
            }
        }

        public static void WriteBytesChar(byte[] buff, int offset, char value)
        {
            WriteBytesInt16(buff, offset, (short)value);
        }
        public unsafe static void WriteBytesInt16(byte[] buff, int offset, short value)
        {
            if (buff.Length >= offset + sizeof(short))
            {
                fixed (byte* b = &buff[offset])
                {
                    *((short*)b) = value;
                }
            }
        }
        public unsafe static void WriteBytesInt32(byte[] buff, int offset, int value)
        {
            if (buff.Length >= offset + sizeof(int))
            {
                fixed (byte* b = &buff[offset])
                {
                    *((int*)b) = value;
                }
            }
        }
        public unsafe static void WriteBytesInt64(byte[] buff, int offset, long value)
        {
            if (buff.Length >= offset + sizeof(long))
            {
                fixed (byte* b = &buff[offset])
                {
                    *((long*)b) = value;
                }
            }
        }
        public unsafe static void WriteBytesUInt16(byte[] buff, int offset, ushort value)
        {
            if (buff.Length >= offset + sizeof(ushort))
            {
                fixed (byte* b = &buff[offset])
                {
                    *((ushort*)b) = value;
                }
            }
        }
        public unsafe static void WriteBytesUInt32(byte[] buff, int offset, uint value)
        {
            if (buff.Length >= offset + sizeof(uint))
            {
                fixed (byte* b = &buff[offset])
                {
                    *((uint*)b) = value;
                }
            }
        }
        public unsafe static void WriteBytesUInt64(byte[] buff, int offset, ulong value)
        {
            if (buff.Length >= offset + sizeof(ulong))
            {
                fixed (byte* b = &buff[offset])
                {
                    *((ulong*)b) = value;
                }
            }
        }

        public unsafe static void WriteBytesSingle(byte[] buff, int offset, float value)
        {
            if (buff.Length >= offset + sizeof(float))
            {
                fixed (byte* b = &buff[offset])
                {
                    *((int*)b) = *(int*)&value;
                }
            }
        }

        public unsafe static void WriteBytesDouble(byte[] buff, int offset, double value)
        {
            if (buff.Length >= offset + sizeof(double))
            {
                fixed (byte* b = &buff[offset])
                {
                    *((long*)b) = *(long*)&value;
                }
            }
        }

        public static void WriteString(byte[] buff, int offset, int length, string value)
        {
            //byte[] str = System.Text.Encoding.Default.GetBytes(val);
            //int size = str.Length;
            //if (size > mSize)
            //    size = mSize;
            //Buffer.BlockCopy(str, 0, st, mOffset, size);
        }

        public unsafe static long DoubleToInt64Bits(double value)
        {
            Debug.Assert(BitConverter.IsLittleEndian);
            return *((long*)&value);
        }

        public unsafe static double Int64BitsToDouble(long value)
        {
            Debug.Assert(BitConverter.IsLittleEndian);
            return *((double*)&value);
        }

        public unsafe static bool ToBoolean(byte[] value, int startIndex)
        {
            if (startIndex < 0 || startIndex > value.Length - 1)
            {
                return default(bool);
            }
            return (value[startIndex] == 0) ? false : true;
        }
        public unsafe static char ToChar(byte[] value, int startIndex)
        {
            return (char)ToInt16(value, startIndex);
        }
        public unsafe static double ToDouble(byte[] value, int startIndex)
        {
            if (startIndex < 0 || startIndex > value.Length - 8)
            {
                return default(double);
            }
            long val = ToInt64(value, startIndex);
            return *(double*)&val;
        }
        public unsafe static short ToInt16(byte[] value, int startIndex)
        {
            if (startIndex < 0 || startIndex > value.Length - 2)
            {
                return default(short);
            }
            fixed (byte* pbyte = &value[startIndex])
            {
                if (startIndex % 2 == 0)
                {
                    return *((short*)pbyte);
                }
                else
                {
                    if (BitConverter.IsLittleEndian)
                    {
                        return (short)((*pbyte) | (*(pbyte + 1) << 8));
                    }
                    else
                    {
                        return (short)((*pbyte << 8) | (*(pbyte + 1)));
                    }
                }
            }
        }
        public unsafe static int ToInt32(byte[] value, int startIndex)
        {
            if (startIndex < 0 || startIndex > value.Length - 4)
            {
                return default(int);
            }
            fixed (byte* pbyte = &value[startIndex])
            {
                if (startIndex % 4 == 0)
                {
                    return *((int*)pbyte);
                }
                else
                {
                    if (BitConverter.IsLittleEndian)
                    {
                        return (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24);
                    }
                    else
                    {
                        return (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3));
                    }
                }
            }
        }
        public unsafe static long ToInt64(byte[] value, int startIndex)
        {
            if (startIndex < 0 || startIndex > value.Length - 8)
            {
                return default(long);
            }
            fixed (byte* pbyte = &value[startIndex])
            {
                if (startIndex % 8 == 0)
                {
                    return *((long*)pbyte);
                }
                else
                {
                    if (BitConverter.IsLittleEndian)
                    {
                        int l = (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24);
                        int h = (*(pbyte + 4)) | (*(pbyte + 5) << 8) | (*(pbyte + 6) << 16) | (*(pbyte + 7) << 24);
                        return (long)((uint)l | ((long)h << 32));
                    }
                    else
                    {
                        int h = (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3));
                        int l = (*(pbyte + 4) << 24) | (*(pbyte + 5) << 16) | (*(pbyte + 6) << 8) | (*(pbyte + 7));
                        return (uint)l | ((long)h << 32);

                    }
                }
            }
        }
        public unsafe static float ToSingle(byte[] value, int startIndex)
        {
            int val = ToInt32(value, startIndex);
            return *(float*)&val;
        }

        static char GetHexValue(int i)
        {
            Debug.Assert(i >= 0 && i < 16, "i is out of range.");
            if (i < 10)
            {
                return (char)(i + '0');
            }
            return (char)(i - 10 + 'A');
        }

        public static string ToString(byte[] value)
        {
            return ToString(value, 0, value.Length);
        }
        public static string ToString(byte[] value, int startIndex)
        {
            return ToString(value, startIndex, value.Length - startIndex);
        }
        
        public static string ToString(byte[] value, int startIndex, int length)
        {
            if (length <= 0 || startIndex < 0 || startIndex > value.Length - length)
            {
                return string.Empty;
            }

            int chArrayLength = length * 3;

            if (s_toStringChArray.Length < chArrayLength)
            {
                s_toStringChArray = new char[chArrayLength];
            }

            Array.Clear(s_toStringChArray, 0, s_toStringChArray.Length);

            int i = 0;
            int index = startIndex;
            for (i = 0; i < chArrayLength; i += 3)
            {
                byte b = value[index++];
                s_toStringChArray[i] = GetHexValue(b / 16);
                s_toStringChArray[i + 1] = GetHexValue(b % 16);
                s_toStringChArray[i + 2] = '-';
            }
            
            return new String(s_toStringChArray, 0, chArrayLength - 1);
        }
        public unsafe static ushort ToUInt16(byte[] value, int startIndex)
        {
            return (ushort)ToInt16(value, startIndex);
        }
        public unsafe static uint ToUInt32(byte[] value, int startIndex)
        {
            return (uint)ToInt32(value, startIndex);
        }
        public unsafe static ulong ToUInt64(byte[] value, int startIndex)
        {
            return (ulong)ToInt64(value, startIndex);
        }
    }
}

